package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vos.WmNewsVo;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper,WmNews> implements WmNewsService {


    @Autowired
    WmMaterialMapper wmMaterialMapper;

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    /**
     * 查询文章列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findAll(WmNewsPageReqDto dto) {
        //1.检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();

        //2.封装查询条件
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        //文章标题模糊查询
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }
        //根据频道查询
        if (dto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }
        //根据文章状态查询
        if (dto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, dto.getStatus());
        }
        //根据发布时间查询
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
            wrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getBeginPubDate());
        }
        //在本地线程中查询user对象（只能查到自己发布的文章）
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN, "需要登录后才能查看文章");
        }
        wrapper.eq(WmNews::getUserId, user.getId());
        //按照创建日期倒序
        wrapper.orderByDesc(WmNews::getCreatedTime);
        Page<WmNews> pageReq = new Page<>(dto.getPage(), dto.getSize());

        //3.执行查询
        IPage<WmNews> pageResult =page(pageReq,wrapper);

        //4.返回封装查询结果
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), pageResult.getTotal());
        responseResult.setData(pageResult.getRecords());
        //处理文章图片
            /*Respons.setHost(ResponseParsers.GetBucketWebsiteResponseParser);
            return result;*/
    return responseResult;
    }

    /**
     * 提交文章
     * @param dto
     * @return
     */
    @Override
    public ResponseResult submitNews(WmNewsDto dto) {

        //1.检查参数(内容,状态,登录用户)
            //检查内容
        if(dto==null || StringUtils.isBlank(dto.getContent())){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"内容为空");
        }
        //设置是否提交参数
        Short isSubmit = dto.getStatus();
            //检查状态(草稿DRAFT_STATUS[0]和待审核SUBMIT_STATUS[1],若非以上两种错误,报参数错误)
        if(!isSubmit.equals(WemediaConstants.WM_NEWS_DRAFT_STATUS)&&
                !dto.getStatus().equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章状态错误");
        }
            //检查用户登录
        WmUser user = WmThreadLocalUtils.getUser();
        if(user==null){
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
            //属性拷贝至WmNews实体类中
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto,wmNews);

            //如果自动生成封面,type设置为null(无图的值在dto中为-1,数据库中不能存储负数)
        if(WemediaConstants.WM_NEWS_SINGLE_IMAGE.equals(dto.getType())){
            dto.setType(null);
        }
            //如果封装images集合不为null, 拼接成字符串 并替换前缀
        List<String> imagesList = dto.getImages();
        if(imagesList!=null && imagesList.size()>0){
            wmNews.setImages(imagesListToStr(imagesList));
        }
        //替换完前缀设置id
        wmNews.setUserId(user.getId());
        // 2. 保存或修改 WmNews对象
        saveOrUpdateWmNews(wmNews);
        //3. 保存文章 和 素材的关联关系
        // 3.1 抽取文章内容中涉及的图片
        List<String> contentImages = parseContentImages(dto.getContent());
        //3.2 保存 文章内容所引用的图片素材的关联关系
        if(isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)&&contentImages!=null&&contentImages.size()>0){
            saveRelativeInfoForContent(contentImages,wmNews);
        }
        //3.3 保存 文章封面所引用的图片素材的关联关系
        if(isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)){
            saveRelativeInfoForCover(contentImages,wmNews,dto);
            kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC,String.valueOf(wmNews.getId()));
            log.info("发表文章成功，并通过kafka通知admin端审核,文章id为========>{}",wmNews.getId());
        }

        return ResponseResult.okResult();
    }

    @Autowired
    private KafkaTemplate kafkaTemplate;


      //引用配置文件的website

    @Value("${file.oss.web-site}")
    String webSite;
    /**
     * 将图片列表集合  用逗号拼接成 一个字符串  并且替换前缀路径
     * @param imagesList
     * @return
     */
    private String imagesListToStr(List<String> imagesList) {
        return imagesList.stream()  // 获取数据流
                .map(url -> url.replaceAll(webSite,""))  //将流中的url路径 替换掉访问前缀
                .collect(Collectors.joining(",")); // 将所有路径 用逗号拼接成字符串
    }

    /**
     * 根据id查找文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult findWmNewsById(Integer id) {
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章ID不能为空");
        }
        WmNews wmNews = getById(id);
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"该文章不存在");
        }
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(webSite);
        return responseResult;
    }

    /**
     * 根据id删除文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult delNews(Integer id) {
        //1.检查参数
        if(id ==null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章id不能为空");
        }
        WmUser user = WmThreadLocalUtils.getUser();
        if(user ==null){
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        //2.根据id查询自媒体文章
        WmNews wmNews = getById(id);
        if(wmNews ==null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        //3.判断文章状态（为发布且已上架）
        if(wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS)&&wmNews.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_UP)){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"发布且上架状态的文章不可以删除");
        }
            //判断是否登录用户上传的
        if(!wmNews.getUserId().equals(user.getId())){
            throw new CustomException(AppHttpCodeEnum.NO_OPERATOR_AUTH,"无权限，无法删除");
        }
        //4.删除文章数据，及关联数据
        removeById(id);
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,id));
        return ResponseResult.okResult();
    }


    /**
     * 文章上下架
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {

        //1.检查参数(1或者0)
        if(dto.getId()==null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章id不能为空");
        }
        if(!WemediaConstants.WM_NEWS_ENABLE_UP.equals(dto.getEnable())
                &&!WemediaConstants.WM_NEWS_ENABLE_DOWN.equals(dto.getEnable())){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章上下状态错误");
        }
        //2.根据ID查询文章
        WmNews wmNews = getById(dto.getId());
        if(wmNews==null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        //3.检查status是否为9(发布状态才能上下架)
        if(!wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS)){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"只有发布状态的文章才可以上下架");
        }
        //4.如果校验状态成功,则上下架操作
        LambdaUpdateWrapper<WmNews> updateWrapper = Wrappers.<WmNews>lambdaUpdate();
        updateWrapper.set(WmNews::getEnable,dto.getEnable());
        if(dto.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_DOWN)){
            updateWrapper.set(WmNews::getStatus,WemediaConstants.WM_NEWS_SUMMIT_STATUS);
        }
        updateWrapper.eq(WmNews::getId,dto.getId()).eq(WmNews::getUserId,WmThreadLocalUtils.getUser().getId());
        update(updateWrapper);

        //kafka异步通知article
        if(wmNews.getArticleId()!=null){
            Map<String,Object> mesMap = new HashMap<>();
            mesMap.put("enable",dto.getEnable());
            mesMap.put("articleId",wmNews.getArticleId());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(mesMap));
            log.info("==================>kafka异步通知上下架消息至article");
        }

        return ResponseResult.okResult();
    }

    /**
     * 查找待审核文章id
     * @return
     */
    @Override
    public List<Integer> findRelease() {
        List<WmNews> list =
                this.list(Wrappers.<WmNews>lambdaQuery().in(WmNews::getStatus,4,8).le(WmNews::getPublishTime,new Date())
                .select(WmNews::getId)); //减少查询字段,提高查询效率

        return list.stream().map(WmNews::getId).collect(Collectors.toList());
    }

    /**
     * 查询文章列表hi
     * @param dto
     * @return
     */

    @Autowired
    WmNewsMapper wmNewsMapper;

    @Override
    public ResponseResult findList(NewsAuthDto dto) {
        //1.检验参数
        dto.checkParam();
        //2.封装查询条件(分装关键字模糊查询)
        Integer currentPage = dto.getPage();
        dto.setPage((currentPage-1)*dto.getSize());

        if (StringUtils.isNotBlank(dto.getTitle())) {
            dto.setTitle("%"+dto.getTitle()+"%");
        }
        //3.调用mapper执行查询,返回分页对象
        List<WmNewsVo> listAndPage = wmNewsMapper.findListAndPage(dto);
        long listCount = wmNewsMapper.findListCount(dto);
        PageResponseResult pageResponseResult = new PageResponseResult(currentPage,dto.getSize(),listCount);
        pageResponseResult.setData(listAndPage);
        pageResponseResult.setHost(webSite);
        return pageResponseResult;
    }

    /**
     * 查询文章详情vo对象
     * @param id
     * @return
     */
    @Autowired
    WmUserMapper wmUserMapper;

    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        //1.根据id查询wmNews对象,如果查询不到返回错误
        WmNews wmNews = this.getById(id);
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"未发现该文章信息");
        }
        //2.根据wmNews中的wmUserId查询wmUser信息
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if(wmUser == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章关联的作者信息不存在");
        }
        //3.封装WmNewsVo对象(属性拷贝+作者名称封装)
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews,wmNewsVo);
        wmNewsVo.setAuthorName(wmUser.getName());
        ResponseResult responseResult = ResponseResult.okResult(wmNewsVo);
        responseResult.setHost(webSite);
        return responseResult;
    }

    /**
     * 管理修改文章状态
     * @param status
     * @param dto
     * @return
     */
    @Override
    public ResponseResult updateStatus(Short status, NewsAuthDto dto) {
        //1.检查参数
        if(dto.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章id不存在");
        }
        //2.通过id获取文章对象,如果不存在则返回错误
        WmNews wmNews = this.getById(dto.getId());
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        //3.修改文章状态(2驳回,4通过)
        wmNews.setStatus(status);
        if(StringUtils.isNotBlank(dto.getMsg())){
            wmNews.setReason(dto.getMsg());
        }
        this.updateById(wmNews);
        //返回对象
        return ResponseResult.okResult();
    }


    /**
     * 保存封面和素材库的关联关系
     * @param contentImages
     * @param wmNews
     * @param dto
     */
    private void saveRelativeInfoForCover(List<String> contentImages, WmNews wmNews, WmNewsDto dto) {
        List<String> imagesList = dto.getImages();
        // 判断是否自动生成封面(如果type类型为-1根据规则自动生成封面)
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            if(contentImages!=null && contentImages.size()>0){
                int size = contentImages.size();
                //如果图片数大于3.取内容中前3张图片作为封面
                if(size>2){
                    imagesList = contentImages.stream().limit(3).collect(Collectors.toList());
                    wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                }else if(size>0 && size <=2){
                    //如果图片数小于3.取内容中1张图片作为封面
                    imagesList = contentImages.stream().limit(1).collect(Collectors.toList());
                    wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                }
            }else {
                //无封面
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            //将封面图片集合 转为逗号拼接的字符串
            wmNews.setImages(imagesListToStr(imagesList));  // 图片字符串
            updateById(wmNews);
        }
        if(imagesList!=null && imagesList.size()>0){
            //确保替换掉路径前缀
            imagesList = imagesList.stream().map(url -> url.replaceAll(webSite,"")).collect(Collectors.toList());
            //保存关联关系
            saveRelativeInfo(imagesList,wmNews.getId(),WemediaConstants.WM_IMAGE_REFERENCE);
        }
    }

    /**
     * 保存内容中引用素材的关联关系
     * @param contentImages
     * @param wmNews
     */
    private void saveRelativeInfoForContent(List<String> contentImages, WmNews wmNews) {
        saveRelativeInfo(contentImages,wmNews.getId(),WemediaConstants.WM_CONTENT_REFERENCE);
    }

    /**
     * 保存文章和素材的关联关系
     * @param materialUrls  素材的图片路径集合
     * @param newsId  文章的id
     * @param type  引用的类型   0 内容引用    1 封面引用
     */
    public void saveRelativeInfo(List<String> materialUrls,Integer newsId,Short type){
        // 根据素材路径集合  及 登录用户userId 查询素材集合
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery()
                .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
                .in(WmMaterial::getUrl, materialUrls));
        if(wmMaterials == null || wmMaterials.size() == 0){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"引用的素材不存在");
        }
        // 将素材列表转为map集合   key: 图片url路径       value: 素材的id
        Map<String, Integer> urlAndIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        // 查看引用素材路径列表 ，在map中是否引用，如果有没有引用的 抛出异常
        List<Integer> materialIds = new ArrayList<>();
        materialUrls.forEach(url -> {
            if(!urlAndIdMap.containsKey(url)){
                throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"引用的素材不存在");
            }
            materialIds.add(urlAndIdMap.get(url));
        });
        // 收集所有素材ID组成一个集合
        wmNewsMaterialMapper.saveRelations(materialIds,newsId,type);
    }

    /**
     * 抽取文章引用的图片
     * @param content
     * @return
     */
    private List<String> parseContentImages(String content) {
        // 使用fastjson 将文章内容转为list集合 集合中的每一个元素 都是一个map
        // map :    type ( text  或  image)      value
        List<Map> contentImages = JSONArray.parseArray(content,Map.class);
        List<String> contentImageList = contentImages.stream()   // 定义流
                .filter(map -> WemediaConstants.WM_NEWS_TYPE_IMAGE.equals (map.get("type"))) // 过滤 保留 type=image
                .map(map -> (String) map.get("value"))  // 映射 获取每个map中 value的值
                .map(url -> url.replaceAll(webSite, "")) // 替换掉路径的访问前缀
                .collect(Collectors.toList()); // 收集到集合中
        return contentImageList;
    }

    /**
     * 保存或修改文章
     * @param wmNews
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {
        // 1. 补全wmNews的信息(创建\提交\时间  允许上架状态)
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable(WemediaConstants.WM_NEWS_ENABLE_UP);
        // 2. 判断id的值是否为空,若为空,新增字段,不为空,删除之前的关联关系,在进行修改操作
        if(wmNews.getId()==null){
            save(wmNews);
        }else {
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
            updateById(wmNews);
        }
    }

}
